#include "driver_function.h"

/*
 * DVP_SCL   ----> PB10
 * DVP_SDA   ----> PB11
 * DVP_VSYNC ----> PA5
 * DVP_HSYNC ----> PA4
 * DVP_PCLK  ----> PA6
 * DVP_XCLK  ----> PA8
 * DVP_D7    ----> PB9
 * DVP_D6    ----> PB8
 * DVP_D5    ----> PB3
 * DVP_D4    ----> PC11
 * DVP_D3    ----> PC9
 * DVP_D2    ----> PC8
 * DVP_D1    ----> PA10
 * DVP_D0    ----> PA9
 * DVP_RST   ----> PE6
 * DVP_PWDN  ----> PA7
 * */
#define  CAMERA_PWDN_SET(val)    (val?GPIO_SetBits(GPIOA,GPIO_Pin_7):GPIO_ResetBits(GPIOA,GPIO_Pin_7))
#define  CAMERA_RESET_SET(val)   (val?GPIO_SetBits(GPIOE,GPIO_Pin_6):GPIO_ResetBits(GPIOE,GPIO_Pin_6))
#define  CAMERA_MISO_GET         (GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_5))


#define  CAMERA_OV7670_WRITE_ADDR 0x42
#define  CAMERA_OV7670_READ_ADDR  0x43
#define  CAMERA_OV7670_CMD_MID_H  0x1C
#define  CAMERA_OV7670_CMD_MID_L  0x1D
#define  CAMERA_OV7670_CMD_PID_H  0x0A
#define  CAMERA_OV7670_CMD_PID_L  0x0B


/*   OV7670 SCCB function  */
#define  CAMERA_SCCB_SCL_SET(val)    (val?GPIO_SetBits(GPIOB,GPIO_Pin_10):GPIO_ResetBits(GPIOB,GPIO_Pin_10))
#define  CAMERA_SCCB_SDA_SET(val)    (val?GPIO_SetBits(GPIOB,GPIO_Pin_11):GPIO_ResetBits(GPIOB,GPIO_Pin_11))
#define  CAMERA_SCCB_SDA_GET         (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11))


static void driver_camera_out_xclk()
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    RCC_PLL2Cmd(DISABLE); //turn off PLL2
    RCC_PREDIV2Config(RCC_PREDIV2_Div2);
    RCC_PLL2Config(RCC_PLL2Mul_6);
    RCC_PLL2Cmd(ENABLE);

    /* MCO Output GPIOA 8 */
    RCC_MCOConfig(RCC_MCO_PLL2CLK); //24Mhz = HSE  / 2 = 4Mhz * 6 = 24Mhz
}

static void driver_camera_sccb_set_sda_in()
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

static void driver_camera_sccb_set_sda_out()
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    CAMERA_SCCB_SDA_SET(0);
}

static void driver_camera_sccb_gpio_init()
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_10 | GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    CAMERA_SCCB_SCL_SET(1);
    CAMERA_SCCB_SDA_SET(1);  //IELE status is SDA and SCL is high level

}

static void driver_camera_sccb_start_signal(void)
{
    // when SCL high ,SDA from high to low
    CAMERA_SCCB_SDA_SET(1);
    CAMERA_SCCB_SCL_SET(1);
    driver_timer_delay_us(50);

    CAMERA_SCCB_SDA_SET(0);
    driver_timer_delay_us(50);

    CAMERA_SCCB_SCL_SET(0); //start send data
}

static void driver_camera_sccb_stop_signal(void)
{
    // when SCL high ,SDA from low to high
    CAMERA_SCCB_SDA_SET(0);
    driver_timer_delay_us(50);

    CAMERA_SCCB_SCL_SET(1);
    driver_timer_delay_us(50);

    CAMERA_SCCB_SDA_SET(1);
    driver_timer_delay_us(50);
}
static void driver_camera_sccb_noack_signal()
{
    driver_timer_delay_us(50);
    CAMERA_SCCB_SDA_SET(1);
    CAMERA_SCCB_SCL_SET(1);
    driver_timer_delay_us(50);

    CAMERA_SCCB_SCL_SET(0);
    driver_timer_delay_us(50);

    CAMERA_SCCB_SDA_SET(0);
    driver_timer_delay_us(50);
}

static uint8_t driver_camera_sccb_write_date(uint8_t date)
{
    uint8_t i,ret;
    for(i=0;i<8;i++) {
        if(date & 0x80) CAMERA_SCCB_SDA_SET(1);
        else CAMERA_SCCB_SDA_SET(0);
        date <<= 1;
        driver_timer_delay_us(50);

        CAMERA_SCCB_SCL_SET(1);
        driver_timer_delay_us(50);

        CAMERA_SCCB_SCL_SET(0);
    }

    driver_camera_sccb_set_sda_in();
    driver_timer_delay_us(50);

    CAMERA_SCCB_SCL_SET(1);
    driver_timer_delay_us(100);

    if (CAMERA_SCCB_SDA_GET) ret = 1; //send failed
    else ret = 0; //send successful

    CAMERA_SCCB_SCL_SET(0);
    driver_camera_sccb_set_sda_out();
    return ret;
}

static uint8_t driver_camera_sccb_read_date(void)
{
    uint8_t i,ret =0;
    driver_camera_sccb_set_sda_in();

    for (i=0;i<8;i++) {
        driver_timer_delay_us(50);
        CAMERA_SCCB_SCL_SET(1);
        driver_timer_delay_us(50);
        ret = ret << 1;
        if (CAMERA_SCCB_SDA_GET) ret++;
        driver_timer_delay_us(50);
        CAMERA_SCCB_SCL_SET(0);
    }
    driver_camera_sccb_set_sda_out();
    return ret;
}

static uint8_t driver_camera_sccb_write_reg(uint8_t reg,uint8_t val)
{
    uint8_t ret = 0;
    driver_camera_sccb_start_signal();
    if(driver_camera_sccb_write_date(CAMERA_OV7670_WRITE_ADDR)) { //send failed
        driver_camera_sccb_stop_signal();
        printf("driver_camera_sccb_write_reg send CAMERA_OV7670_WRITE_ADDR failed\r\n");
        return 1;
    }
    driver_timer_delay_us(100);

    if(driver_camera_sccb_write_date(reg)) {
        driver_camera_sccb_stop_signal();
        printf("driver_camera_sccb_write_reg send reg:0x%x failed\r\n",reg);
        return 1;
    }
    driver_timer_delay_us(100);

    if(driver_camera_sccb_write_date(val)) {
        driver_camera_sccb_stop_signal();
        printf("driver_camera_sccb_write_reg send val:0x%x failed\r\n",val);
        return 1;
    }
    driver_camera_sccb_stop_signal();
    return ret;
}

static uint8_t driver_camera_sccb_read_reg(uint8_t reg)
{
    uint8_t val = 0;
    driver_camera_sccb_start_signal();

    if( driver_camera_sccb_write_date(CAMERA_OV7670_WRITE_ADDR) ) {
        driver_camera_sccb_stop_signal();
        printf("driver_camera_sccb_read_reg send CAMERA_OV7670_WRITE_ADDR failed\r\n");
        return 0xFF;//failed
    }
    driver_timer_delay_us(100);

    if (driver_camera_sccb_write_date(reg) ) {
        driver_camera_sccb_stop_signal();
        printf("driver_camera_sccb_read_reg send reg:0x%x failed\r\n",reg);
        return 0xFF;//failed
    }
    driver_timer_delay_us(100);
    driver_camera_sccb_stop_signal();

    driver_timer_delay_us(100);

    driver_camera_sccb_start_signal();

    if (driver_camera_sccb_write_date(CAMERA_OV7670_READ_ADDR) ) {
        driver_camera_sccb_stop_signal();
        printf("driver_camera_sccb_read_reg send CAMERA_OV7670_READ_ADDR failed\r\n");
        return 0xFF;//failed
    }
    driver_timer_delay_us(100);

    val = driver_camera_sccb_read_date();
    driver_camera_sccb_noack_signal();
    driver_camera_sccb_stop_signal();
    return val;
}

/* DVP funcition */
static void driver_camera_dvp_gpio_init()
{
    GPIO_InitTypeDef  GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOE, ENABLE);

    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOE, &GPIO_InitStructure);
}

void driver_camera_init()
{
    driver_camera_out_xclk();
    driver_camera_sccb_gpio_init();
    driver_camera_dvp_gpio_init();

    CAMERA_PWDN_SET(0); // 0: work mode; 1: power down
    driver_timer_delay_us(10000);

    CAMERA_RESET_SET(0);
    driver_timer_delay_us(20000);
    CAMERA_RESET_SET(1);
    driver_timer_delay_us(20000);

    if (driver_camera_sccb_write_reg(0x12,0x80)) {
        printf("soft reset ov7670 failed\r\n");
        return;
    }
    driver_timer_delay_us(50000);
}

void driver_camera_read_id()
{
    uint16_t MID,PID;       // MID = 0x7FA2, PID = 0x7673
    MID = driver_camera_sccb_read_reg(CAMERA_OV7670_CMD_MID_H);
    MID <<= 8;
    MID |= driver_camera_sccb_read_reg(CAMERA_OV7670_CMD_MID_L);

    PID = driver_camera_sccb_read_reg(CAMERA_OV7670_CMD_PID_H);
    PID <<= 8;
    PID |= driver_camera_sccb_read_reg(CAMERA_OV7670_CMD_PID_L);
    printf("Get MID is %#x PID is %#x\r\n",MID,PID);
}
